import { PureComponent } from 'react';
import {
  Scene, OrthographicCamera, BoxGeometry, Line, Vector3,
  Mesh, WebGLRenderer, MeshBasicMaterial, PlaneGeometry,
  MeshLambertMaterial, SpotLight, AmbientLight
} from 'three';
import Stats from 'stats.js';
import { requestAnimationFrame } from '@/utils/requestAnimationFrame';

const drawAxes = scene => {
  const xGeo = new BoxGeometry();
  const xGeoPoints = [
    new Vector3(0, 0, 0),
    new Vector3(6, 0, 0)
  ];
  xGeo.setFromPoints(xGeoPoints);
  const xMat = new MeshBasicMaterial({
    color: 0xff0000
  });
  const xAxis = new Line(xGeo, xMat);
  scene.add(xAxis);

  const yGeo = new BoxGeometry();
  const yGeoPoints = [
    new Vector3(0, 0, 0),
    new Vector3(0, 6, 0)
  ];
  yGeo.setFromPoints(yGeoPoints);
  const yMat = new MeshBasicMaterial({
    color: 0x00ff00
  });
  const yAxis = new Line(yGeo, yMat);
  scene.add(yAxis);

  const zGeo = new BoxGeometry();
  const zGeoPoints = [
    new Vector3(0, 0, 0),
    new Vector3(0, 0, 6)
  ];
  zGeo.setFromPoints(zGeoPoints);
  const zMat = new MeshBasicMaterial({
    color: 0x00ccff
  });
  const zAxis = new Line(zGeo, zMat);
  scene.add(zAxis);
};

const stats = new Stats();

const scene = new Scene();

drawAxes(scene);

let renderer = null;

let animateId = 0;

let alpha = 0;

//设置一个偏移量, 让物体位于坐标轴之上, 静止或运动时都不再被坐标轴切割
const offset = 0.5;

const camera = new OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
camera.position.set(5, 15, 25);
camera.lookAt(0, 0, 0);

//平面
const planeGeo = new PlaneGeometry(8, 8, 16, 16);
const planeMat = new MeshLambertMaterial({
  color: 0xcccccc
});
const plane = new Mesh(planeGeo, planeMat);
plane.rotation.x = - Math.PI / 2;
plane.position.y = -1;
plane.receiveShadow = true;
scene.add(plane);

//物体
const geometry = new BoxGeometry(1, 1, 1);
const material = new MeshLambertMaterial({
  color: 0x00ff00
});
const mesh = new Mesh(geometry, material);
mesh.position.x = 2;
//这个设置教程案例中没有, 但我画了坐标轴, 因此设置一下物体的y坐标,
//让物体运动起来处于平面之上, 不再被坐标轴切割
mesh.position.y = offset;
mesh.castShadow = true;
scene.add(mesh);

//聚光灯
const light = new SpotLight(0xffff00, 1, 100, Math.PI / 6, 25);
light.position.set(2, 5, 3);
light.target = mesh; //让聚光灯始终照着这个物体
light.castShadow = true;

//聚光灯的其他设置
//shadowCameraNear也已经被废弃, 这里使用新的api, 下同
light.shadow.camera.near = 2;
light.shadow.camera.far = 10;
light.shadow.camera.fov = 30;
light.shadow.camera.visible = true;

//shadowMapWidth也已经被废弃, 这里使用新的api, 下同
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
//同时也没有了shadowDarkness属性
// light.shadowDarkness = 0.3;

scene.add(light);

//环境光
//这里使用了两个光源: 聚光灯, 环境光
const ambientLight = new AmbientLight(0x666666);
scene.add(ambientLight);

const animate = () => {
  stats.begin();

  alpha += 0.01;

  //一样的一周之后重置的操作
  if(alpha > Math.PI * 2) {
    alpha -= Math.PI * 2;
  }

  //这里需要注意, 如果用的rotation.y则是围绕y轴旋转, 可以理解为自转, 这个api在导入外部模型那接触过, 就是让茶壶旋转
  //而这里则是公转, 而x z坐标均有变化, 所以不能用rotation.y
  //同时还要注意, 教程中这里的y坐标恒为0, 但为了让物体在运动时也不被坐标轴切割, 这里的y坐标应为偏移量
  mesh.position.set(2 * Math.cos(alpha), offset, 2 * Math.sin(alpha));

  renderer.render(scene, camera);
  stats.end();
  animateId = requestAnimationFrame(animate);
};

const startAnimate = () => {
  if(animateId) return;
  animateId = requestAnimationFrame(animate);
};

const stopAnimate = () => {
  if(!animateId) return;
  cancelAnimationFrame(animateId);
  animateId = 0;
};

class Index extends PureComponent {

  componentDidMount() {
    renderer = new WebGLRenderer({
      canvas: document.getElementById('mainCanvas')
    });
    //目前threejs的版本中shadowMapEnabled已经被废弃了, 这是新的api:
    renderer.shadowMap.enabled = true;
    //shadowMapEnabled shadowMapSoft都已经被废弃,
    //同时shadowMap中并没有soft
    // renderer.shadowMap.soft = true;
    renderer.render(scene, camera);

    const fpsContainer = document.getElementById('fps-container');
    stats.domElement.style.position = '';
    fpsContainer.appendChild(stats.dom);
  }

  render() {

    return (
      <div>
        <div>
          <button
            onClick={startAnimate}
          >开始</button>
          <button
            onClick={stopAnimate}
          >停止</button>
        </div>
        <div
          id="fps-container"
          style={{
            display: 'flex',
            padding: '5px'
          }}
        />
        <canvas
          id="mainCanvas"
          width="400"
          height="300"
        />
      </div>
    );
  }
}

export default Index;